
/*
 * Project miaw : mi_ref.h
 * Created: 2022/3/11
 * This program is unlicensed. See LICENSE for more.
 */


#ifndef MIAW_MI_REF_H
#define MIAW_MI_REF_H
ENGINE_BEGIN

class Referencable ;
template<typename T> concept DerivedFromReferencable = std::is_base_of_v<Referencable, T>;
template<DerivedFromReferencable T> class Ref;
template<DerivedFromReferencable T> class ConstRef;

class RefDirect {
    Referencable * m_p;
    mutable unsigned m_cnt;
public:
    inline explicit RefDirect (Referencable * p) {
        m_p = p;
    }
    // Cant be copied, nor can it be moved.
    RefDirect (const RefDirect & b) = delete ;
    RefDirect (const RefDirect && b) = delete ;
    inline Referencable * get () const {return m_p;}
protected:
    template<DerivedFromReferencable T> friend class Ref;
    template<DerivedFromReferencable T> friend class ConstRef;
    friend class Referencable;
    inline void increment () const {m_cnt ++;}
    inline void decrement () const {
        if(!(--m_cnt)) delete this;
    }
private:
    inline ~RefDirect () {
        engineEnsure(m_cnt == 0);
    }
};

/// \note 所有Referencable在被创建后其内存地址都不应发生变动，
/// 因此不能用vector等STL容器存储它们
class Referencable : public virtual Object {
    DECLARE_CLASS();
public:
    Referencable (const Referencable & r) = delete;
    Referencable (const Referencable && r) = delete;
    Referencable & operator = (const Referencable & r) = delete;
    Referencable & operator = (const Referencable && r) = delete;
    Referencable () {
        m_direct_reference = new RefDirect(this);
        m_direct_reference->increment();
    }
    ~Referencable () override {
        m_direct_reference->m_p = nullptr;
        m_direct_reference->decrement();
    }
    inline bool referenced () const {
        return m_direct_reference->m_cnt > 1;
    }
protected:

    const RefDirect * getDirectReference () const ;
    template<DerivedFromReferencable T> friend class Ref;
    template<DerivedFromReferencable T> friend class ConstRef;
    friend class RefDirect;
private:
    RefDirect * m_direct_reference;
};

// TODO 去掉对dynamic cast的使用，这会消耗额外的运行时

// Ref 为反射式指针，当其指向的Referencable析构时会被自动清为0
// Ref可以快速地复制、重新赋值等等，但是访问其指向的元素需要两次内存访问（相比于普通指针只需要一次内存访问）
// 所以如果需要连续大量的访问其指向的元素时，建议使用get函数获取普通指针之后再访问

template<DerivedFromReferencable T> class Ref {
protected:
    const RefDirect * m_p;
    friend class Referencable ;
    template<DerivedFromReferencable R> friend class ConstRef;
public:
    inline Ref (): m_p(nullptr) {}

    inline Ref (T * p) { // NOLINT
        if(p) {
            m_p = p->getDirectReference();
            m_p->increment();
        } else m_p = nullptr;
    }
    inline Ref (const Ref & b) {
        m_p = b.m_p;
        if(m_p) m_p->increment();
    }
    inline Ref (Ref && b)  noexcept {
        m_p = b.m_p;
        b.m_p = nullptr;
    }
    inline ~Ref () {if(m_p) m_p->decrement();}
    inline Ref & operator = (const Ref & b) {
        if(&b == this) return *this;
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        if(m_p) m_p->increment();
        return *this;
    }
    inline Ref & operator = (Ref && b) noexcept {
        if(&b == this) return *this;
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        b.m_p = nullptr;
        return *this;
    }
    inline Ref & operator = (T * b) {
        if(!b) {
            if(m_p) m_p->decrement();
            m_p = nullptr;
        } else {
            if(m_p) m_p->decrement();
            m_p = b->getDirectReference();
            m_p->increment();
        }
        return *this;
    }
    inline bool empty () {return !m_p || m_p->get() == nullptr;}
    inline bool operator == (const Ref & b) const {
        return m_p == b.m_p;
    }
    inline bool operator != (const Ref & b) const {
        return m_p != b.m_p;
    }
    inline bool operator == (const T * b) const {return m_p ? m_p->get() == b : b == nullptr;}
    inline bool operator != (const T * b) const {return m_p ? m_p->get() != b : b != nullptr;}
    inline bool operator ! () const {return !(m_p && m_p->get());}
    inline operator bool () const {return m_p && m_p->get();} // NOLINT
    inline T * get () const {return m_p ? dynamic_cast<T*>(m_p->get()) : nullptr;}
    inline T * operator -> () const {return m_p ? dynamic_cast<T*>(m_p->get()) : nullptr;}
    inline void set (T * b) {*this = b;}
    inline T & operator * () {return *dynamic_cast<T*>(m_p->get());}
    inline const T & operator * () const {return *dynamic_cast<T*>(m_p->get());}
};

template<DerivedFromReferencable T> class ConstRef {
protected:
    const RefDirect * m_p;
    friend class Referencable ;
public:
    inline ConstRef (): m_p(nullptr) {}
    inline ConstRef (const T * p) { // NOLINT
        if(p) {
            m_p = p->getDirectReference();
            m_p->increment();
        } else m_p = nullptr;
    }
    inline explicit ConstRef (const Ref<T> & b) {
        m_p = b.m_p;
        if(m_p) m_p->increment();
    }
    inline explicit ConstRef (Ref<T> && b) {
        m_p = b.m_p;
        b.m_p = nullptr;
    }
    inline ConstRef (const ConstRef<T> & b) {
        m_p = b.m_p;
        if(m_p) m_p->increment();
    }
    inline ConstRef (ConstRef && b) noexcept {
        m_p = b.m_p;
        b.m_p = nullptr;
    }
    inline ~ConstRef () {if(m_p) m_p->decrement();}
    inline ConstRef & operator = (const ConstRef & b) {
        if(&b == this) return *this;
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        if(m_p) m_p->increment();
        return *this;
    }
    inline ConstRef & operator = (ConstRef && b) noexcept {
        if(&b == this) return *this;
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        b.m_p = nullptr;
        return *this;
    }
    inline ConstRef & operator = (const Ref<T> & b) {
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        if(m_p) m_p->increment();
        return *this;
    }
    inline ConstRef & operator = (Ref<T> && b) noexcept {
        if(m_p) m_p->decrement();
        m_p = b.m_p;
        b.m_p = nullptr;
        return *this;
    }
    inline ConstRef & operator = (const T * b) {
        if(!b) {
            if(m_p) m_p->decrement();
            m_p = nullptr;
        } else {
            if(m_p) m_p->decrement();
            m_p = b->getDirectReference();
            m_p->increment();
        }
        return *this;
    }
    inline bool empty () {return !m_p || m_p->get() == nullptr;}
    inline bool operator == (const Ref<T> & b) const {
        return m_p == b.m_p;
    }
    inline bool operator != (const Ref<T> & b) const {
        return m_p != b.m_p;
    }
    inline bool operator == (const ConstRef<T> & b) const {
        return m_p == b.m_p;
    }
    inline bool operator != (const ConstRef<T> & b) const {
        return m_p != b.m_p;
    }
    inline bool operator == (const T * b) const {return m_p ? m_p->get() == b : b == nullptr;}
    inline bool operator != (const T * b) const {return m_p ? m_p->get() != b : b != nullptr;}
    inline bool operator ! () const {return !(m_p && m_p->get());}
    inline operator bool () const {return m_p && m_p->get();} // NOLINT
    inline const T * get () const {return m_p ? dynamic_cast<const T*>(m_p->get()) : nullptr;}
    inline const T * operator -> () {return dynamic_cast<const T*>(m_p->get());}
    inline void set (const T * b) {*this = b;}
    inline const T & operator * () const {return *dynamic_cast<T*>(m_p->get());}
};
template<class T>
inline bool operator == (const Ref<T> & a, const ConstRef<T> & b) {
    return a.m_p == b.m_p;
}
template<class T>
inline bool operator != (const Ref<T> & a, const ConstRef<T> & b) {
    return a.m_p != b.m_p;
}

ENGINE_END
#endif //MIAW_MI_REF_H
